Description of Data:

Dataset sourced from: https://github.com/jasonshi10/art_auction_valuation?tab=readme-ov-file

37,638 Unique Rows

23 Columns (Price, Material, Height, etc.)

Data Preparation

library(ggplot2)
library(naniar) # Load nanair for missing data visualization
library(OneR) 
library(tidyverse)
── Attaching core tidyverse packages ────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.0     ✔ readr     2.1.5
✔ lubridate 1.9.3     ✔ stringr   1.5.1
✔ purrr     1.0.2     ✔ tidyr     1.3.1
── Conflicts ──────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ purrr::accumulate()     masks foreach::accumulate()
✖ randomForest::combine() masks dplyr::combine()
✖ neuralnet::compute()    masks dplyr::compute()
✖ mice::filter()          masks dplyr::filter(), stats::filter()
✖ dplyr::lag()            masks stats::lag()
✖ purrr::lift()           masks caret::lift()
✖ randomForest::margin()  masks ggplot2::margin()
✖ xgboost::slice()        masks dplyr::slice()
✖ purrr::when()           masks foreach::when()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(tidytext)
library(dplyr)
# Read in the data 
data <- read.delim("~/Desktop/MachineLearning/Final Project/data.txt")

# Take out data that's not needed (X, yearOfBirth, yearOfDeath, soldTime)
art_data <- data[, c(2:3, 6:11, 14:22)]
# Removed a row due to data being inaccurate. 
art_data <- art_data[-34541, ]

# Look at the structure of art_data 
str(art_data)
'data.frame':   41252 obs. of  17 variables:
 $ artist            : chr  "Mario A" "Mario A" "A E Cremer" "A G Schultz & Co." ...
 $ country           : chr  "Swiss" "Swiss" "French" "American" ...
 $ name              : chr  "The world is beautyful #5" "The world is beautyful #13" "Spot Lights" "Sugar/Sweetmeat Baskets" ...
 $ year              : chr  "2004" "2004" "" "" ...
 $ price             : num  5315 7383 2090 615 8125 ...
 $ material          : chr  "laserchrome_print_diasec" "laserchrom_print_(diasec.)" "black-painted_metal" "Sterling_Silver" ...
 $ height            : chr  "29.53" "29.53" "" "6.5" ...
 $ width             : chr  "39.37" "39.37" "" "5.75" ...
 $ dominantColor     : chr  "yellows" "blacks" "whites" "blacks" ...
 $ brightness        : num  98 73 212 73 216 45 188 94 204 222 ...
 $ ratioUniqueColors : num  0.25 0.19 0.05 0.18 0.02 0.19 0 0 0.12 0.16 ...
 $ thresholdBlackPerc: num  65.7 66.2 20.2 83 11.3 ...
 $ highbrightnessPerc: num  0.75 15.21 0 13.74 0 ...
 $ lowbrightnessPerc : num  21.56 46.5 17.56 35.74 6.27 ...
 $ CornerPer         : num  0.37 1.52 0.39 3.58 1.25 0.34 1.14 0.59 1.08 1.18 ...
 $ EdgePer           : num  4.02 7.28 4.15 13.13 12.95 ...
 $ FaceCount         : num  1 0 0 0 0 0 0 0 0 0 ...
summary(art_data$price)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
       20      1638      6605    241529     23750 119922500 

Some rows in our data are empty but not set to N/A. Need to convert those empty values to N/A

art_data <- as.data.frame(lapply(art_data, function(x) {
  ifelse(x == "", NA, x)
}))

Data Visualization

Visualize our response variable, Price Due to the large range of values in Price, we decided to take the natural log of price \(log(price + 1)\). This will help better visualize price.

summary(art_data$price)
     Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
       20      1638      6605    241529     23750 119922500 

Taking the log(Price + 1), this helped reduce the skew of Price. Making it easier to visualize

Look at the missing values in our data…

# Visualize missing features
feat_vars <- names(art_data)[c(4, 6:17)]
vis_miss(art_data[, feat_vars])


# Visualize missing features with our response variable price
t_bins <- bin(art_data$log_price, nbins = 6, method = "length") # Bin response variable
plot_dat <- cbind.data.frame(t_bins, art_data[, feat_vars])
gg_miss_fct(x = plot_dat, fct = t_bins) +
  labs(x = "Price")

Year, width, and height are the only features that have missing data,with Year having the most missing values. Year having 32% missing, making it a feature we won’t use for building our model.

Find the most common materials used in our dataframe.

art_data$material <- str_replace_all(art_data$material, '_', ' ')

# Figure out the most common words/phrase used in materials column using the tidyverse
word_count <- art_data %>%
  unnest_tokens(word, material) %>%
  anti_join(stop_words, by = "word") %>%
  count(word, sort = TRUE)

# Plot top 10 words/phrases
word_count %>%
  slice_max(n, n = 10) %>%
  ggplot(aes(x = reorder(word, n), y = n)) +
  geom_col() +
  coord_flip() +
  labs(x = "Word", y = "Count", title = "Most Common Words")

Oil is the most used material in our dataset

# Pull out major material categories like oil 
oil <- rep(0, nrow(art_data))
oil[grep("oil", art_data$material)] <- 1
sum(oil[grep("oil", art_data$material)])
[1] 13511
# 13511 Artworks use Oil as a material
# Create Factor column of oil 
art_data$oil <- as.factor(oil)

# Visualize Oil and Price 
g_2 <- ggplot(art_data, aes( y = log_price, x = oil, fill = oil)) + # Set x and fill as disagnosis, y as value
  geom_boxplot() + # Use boxlot
    theme_bw() + # Set theme
  theme(panel.grid.major = element_blank(), # Remove grid
        panel.grid.minor = element_blank(), # Remove grid
        panel.border = element_blank(), # Remove grid
        panel.background = element_blank()) + # Remove grid 
  labs(x = "Oil", title = "Oil vs Price",
       fill = "Oil") + # Set labels
  scale_fill_manual(values = c("1" = "red", "0" = "blue"), # Manually set fill values
                    labels = c("1" = "Oil", "0" = "Other Material Used"))
g_2


# Pull out acrylic in material and visualize 
acrylic <- rep(0, nrow(art_data))
acrylic[grep("acrylic", art_data$material)] <- 1
art_data$acrylic <- as.factor(acrylic)

# Visualize Acrylic and Price 
g_3 <- ggplot(art_data, aes( y = log_price, x = acrylic, fill = acrylic)) + 
  geom_boxplot() + 
    theme_bw() + 
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(), 
        panel.border = element_blank(), 
        panel.background = element_blank()) + 
  labs(x = "Acrylic", title = "Acrylic vs Price",
       fill = "Acrylic") + # Set labels
  scale_fill_manual(values = c("1" = "red", "0" = "blue"), 
                    labels = c("1" = "Acrylic", "0" = "Other Material Used"))
g_3


# Watercolor 
# Pull out watercolor in material and visualize 
watercolor <- rep(0, nrow(art_data))
watercolor[grep("watercolor", art_data$material)] <- 1
art_data$watercolor <- as.factor(watercolor)

# Visualize Acrylic and Price 
g_4 <- ggplot(art_data, aes( y = log_price, x = watercolor, fill = watercolor)) + 
  geom_boxplot() + 
    theme_bw() + 
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(), 
        panel.border = element_blank(), 
        panel.background = element_blank()) + 
  labs(x = "watercolor", title = "watercolor vs Price",
       fill = "watercolor") + # Set labels
  scale_fill_manual(values = c("1" = "red", "0" = "blue"), 
                    labels = c("1" = "watercolor", "0" = "Other Material Used"))
g_4


# Pull out Screenprint in material and visualize 
screenprint <- rep(0, nrow(art_data))
screenprint[grep("screenprint", art_data$material)] <- 1
art_data$screenprint <- as.factor(screenprint)

# Visualize Screenprint and Price 
g_5 <- ggplot(art_data, aes( y = log_price, x = screenprint, fill = screenprint)) + 
  geom_boxplot() + 
    theme_bw() + 
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(), 
        panel.border = element_blank(), 
        panel.background = element_blank()) + 
  labs(x = "screenprint", title = "screenprint vs Price",
       fill = "screenprint") + # Set labels
  scale_fill_manual(values = c("1" = "red", "0" = "blue"), 
                    labels = c("1" = "screenprint", "0" = "Other Material Used"))
g_5



Through these visualizations, we can see that the Material’s used to create an artwork does have an impact on our response variable price.

Understand the relationship with price and other features by visualization.


Brightness and Log_Price

g_7 <- ggplot(art_data,
              aes(y = log_price, 
                  x = brightness)) + 
  geom_point(color = "blue", alpha = 0.10) + 
  geom_smooth(method = 'lm') +
  theme_bw() + 
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) +
  labs(y = "Price", # Set plot labels
       x = "Brightness",
       title = "Brightness of Artwork vs Log_Price")
g_7
`geom_smooth()` using formula = 'y ~ x'

The Brightness of an artwork seems to have a somewhat positive relationship with the log_price

Face Count vs Price

art_data$FaceCount <- as.factor(art_data$FaceCount)
g_8 <- ggplot(art_data, aes( y = log_price, x = FaceCount, fill = FaceCount)) + 
  geom_boxplot() + 
    theme_bw() + 
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(), 
        panel.border = element_blank(), 
        panel.background = element_blank())
g_8

The face count of an artwork seems to have a relationship with log_price, but there are many outliers with log_price and FaceCount

Dominant Color vs Price

art_data$dominantColor <- as.factor(art_data$dominantColor)
g_9 <- ggplot(art_data, aes( y = log_price, x = dominantColor, fill = dominantColor)) + 
  geom_boxplot() + 
    theme_bw() + 
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(), 
        panel.border = element_blank(), 
        panel.background = element_blank())
g_9

Dominant Color seems to play a role in the price of an artwork, but there are many outliers. Ratio of Unique Colors vs Price

g_10 <- ggplot(art_data,
              aes(y = log_price, 
                  x = ratioUniqueColors)) + 
  geom_point(color = "blue", alpha = 0.10) + 
  geom_smooth(method = 'lm') +
  theme_bw() + 
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) +
  labs(y = "Price", # Set plot labels
       x = "Ratio Unique Colors",
       title = "Ratio of Unique Colors vs Log_Price")
g_10
`geom_smooth()` using formula = 'y ~ x'

There seems to be a somewhat negative relationship with the Ratio of Unique Colors and the Price of an Artwork. The less colors used in an artwork or the higher ratio of unique colors, the price of an artwork decreases
Visualize Height/Width with Price

art_data$width <- as.numeric(art_data$width)
Warning: NAs introduced by coercion
art_data$height <- as.numeric(art_data$height)
Warning: NAs introduced by coercion
# Decided to take the log of width & height of an artwork to reduce the skew...
g_12 <- ggplot(art_data,
              aes(y = log_price, 
                  x = log(width))) + 
  geom_point(color = "blue", alpha = 0.10) + 
  geom_smooth(method = 'lm') +
  theme_bw() + 
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) +
  labs(y = "Log_Price", # Set plot labels
       x = "log(Width) of Artwork",
       title = "Log_Width vs Log_Price")
g_12
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 2512 rows containing non-finite outside the scale range (`stat_smooth()`).
Warning: Removed 2512 rows containing missing values or values outside the scale range
(`geom_point()`).

g_13 <- ggplot(art_data,
              aes(y = log_price, 
                  x = log(height))) + 
  geom_point(color = "blue", alpha = 0.10) + 
  geom_smooth(method = 'lm') +
  theme_bw() + 
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) +
  labs(y = "Log_Price", # Set plot labels
       x = "log(Height) of Artwork",
       title = "Log_Height vs Log_Price")
g_13
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 2512 rows containing non-finite outside the scale range (`stat_smooth()`).
Warning: Removed 2512 rows containing missing values or values outside the scale range
(`geom_point()`).

g_14 <- ggplot(art_data,
              aes(y = log_price, 
                  x = log(height*width))) + 
  geom_point(color = "blue", alpha = 0.10) + 
  geom_smooth(method = 'lm') +
  theme_bw() + 
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) +
  labs(y = "Log_Price", # Set plot labels
       x = "Log_Area of Artwork",
       title = "Log_Area vs Log_Price")
g_14
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 2512 rows containing non-finite outside the scale range (`stat_smooth()`).
Warning: Removed 2512 rows containing missing values or values outside the scale range
(`geom_point()`).

There seems to be a somewhat positive relationship with the area of an artwork and its price

Create a new column to identify a well known artist vs not well known artist based on the average price of artworks.
There are 8,608 unique artists in the data-set.

summary(art_data$well_known)
    0     1 
23277 17975 

Visualize Artwork Price and if the Artist is considered well known or not.

art_data$well_known <- as.factor(art_data$well_known)
g_11 <- ggplot(art_data, aes( y = log_price, x = well_known, fill = well_known)) + 
  geom_boxplot() + 
    theme_bw() + 
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(), 
        panel.border = element_blank(), 
        panel.background = element_blank())
g_11

Well known artists have a higher average artwork price compared to lesser known artists.

save(art_data, artist_avg, plot_dat, word_count, data,
     file = "final_project_data.RData")

LS0tCnRpdGxlOiAiVmFsdWluZyBBcnR3b3JrIC0gTWFjaGluZSBMZWFybmluZyBQcm9qZWN0IFByb3Bvc2FsIgphdXRob3I6ICJZdW4tU2hpdWFuIEhzdSBhbmQgRWxpc2FiZXRoIEdhbmd3ZXIiCm91dHB1dDogaHRtbF9ub3RlYm9vawplZGl0b3Jfb3B0aW9uczogCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgojIyBEZXNjcmlwdGlvbiBvZiBEYXRhOgoKRGF0YXNldCBzb3VyY2VkIGZyb206CjxodHRwczovL2dpdGh1Yi5jb20vamFzb25zaGkxMC9hcnRfYXVjdGlvbl92YWx1YXRpb24/dGFiPXJlYWRtZS1vdi1maWxlPgoKMzcsNjM4IFVuaXF1ZSBSb3dzCgoyMyBDb2x1bW5zIChQcmljZSwgTWF0ZXJpYWwsIEhlaWdodCwgZXRjLikKCi0gICBgQXJ0aXN0YC0gQXJ0aXN0IE5hbWUKCi0gICBgQ291bnRyeWAgLSBDb3VudHJ5IGFydGlzdCBpcyBmcm9tCgotICAgYFllYXJvZkJpcnRoYCAtIEFydGlzdCdzIGJpcnRoIHllYXIKCi0gICBgWWVhck9mRGVhdGhgIC0gQXJ0aXN0J3MgZGVhdGggeWVhcgoKLSAgIGBOYW1lYCAtIE5hbWUgb2YgdGhlIGFydHdvcmsKCi0gICBgWWVhcmAgLSBZZWFyIGFydHdvcmsgd2FzIGNyZWF0ZWQKCi0gICBgTWF0ZXJpYWxgIC0gTWF0ZXJpYWxzIHVzZWQgZm9yIHRoZSBBcnR3b3JrCgotICAgYEhlaWdodGAgLSBIZWlnaHQgb2YgYXJ0d29yayBpbiBpbmNoZXMKCi0gICBgV2lkdGhgLSBXaWR0aCBvZiBhcnR3b3JrIGluIGluY2hlcwoKLSAgIGBMaW5rYCAtIExpbmsgdG8gYW4gaW1hZ2Ugb2YgYXJ0d29yawoKLSAgIGBTb3VyY2VgIC0gV2hlcmUgdGhlIGRhdGEgd2FzIG9yaWdpbmFsbHkgc2NyYXBlZCBmcm9tCgotICAgYERvbWluYW50Q29sb3JgIC0gVGhlIGRvbWluYW50IGNvbG9yIGluIGFuIGFydHdvcmsKCi0gICBgQnJpZ2h0bmVzc2AgLSBNZWFuIGJyaWdodG5lc3Mgb2YgYW4gYXJ0d29yay4gQSB2YWx1ZSBjbG9zZXIgdG8gMAogICAgZGVub3RlcyBhIGRhcmsgaW1hZ2UgYW5kIHRoYXQgY2xvc2VyIHRvIDI1NSBpbmRpY2F0ZXMgYSBicmlnaHQgb25lCgotICAgYFJhdGlvVW5pcXVlQ29sb3JzYCAtIFRoZSBudW1iZXIgb2YgdW5pcXVlIGNvbG9ycyBpbiBhbiBpbWFnZSBhcyBhCiAgICByYXRpb24gb2YgdGhlIHRvdGFsIG51bWJlciBvZiBwaXhlbHMKCi0gICBgdGhyZXNob2xkQmxhY2tQZXJjYCAtIElmIHBpeGVsIHZhbHVlIGlzIGdyZWF0ZXIgdGhhbiBhIHRocmVzaG9sZAogICAgdmFsdWUoaGVyZSB3ZSB1c2UgMTI3LCByYW5nZSBmcm9tIDAtMjU1KSwgaXQgaXMgYXNzaWduZWQgb25lIHZhbHVlCiAgICAoMjU1LHdoaXRlKSwgZWxzZSBpdCBpcyBhc3NpZ25lZCBhbm90aGVyIHZhbHVlICgwLGJsYWNrKS4gVGhlbgogICAgY2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIHdoaXRlIG9yIGJsYWNrIGluIHRoZSBpbWFnZSwgYW5kIGdldCB0aGUKICAgIHJhdGlvIG9mIGJsYWNrIHBpeGVscyBpbiB0aGUgZ3JleXNjYWxlIG9mIHBhaW50aW5ncwoKLSAgIGBIaWdoYnJpZ2h0bmVzc1BlcmNgIC0gQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIGJyaWdodG5lc3Mgb2YgZWFjaAogICAgcGFpbnRpbmdzIGFuZCBob3cgbWFueSBwaXhlbHMgaGF2ZSB0d28gdGltZXMgb2YgdGhlIGF2ZXJhZ2UKICAgIGJyaWdodG5lc3MsIHRoZW4gZ2V0IHJhdGlvIG9mIHRoZXNlIHR3byBudW1iZXJzLgoKLSAgIGBMb3dicmlnaHRuZXNzUGVyY2AgLSBDYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgYnJpZ2h0bmVzcyBvZiBlYWNoCiAgICBwYWludGluZ3MgYW5kIGNvdW50IGhvdyBtYW55IHBpeGVscyBoYXZlIGxlc3MgdGhhbiBoYWxmIG9mIHRoZSBtZWFuCiAgICBicmlnaHRuZXNzIG9mIHRoYXQgaW1hZ2UsIHRoZW4gZ2V0IHJhdGlvIG9mIHRoZXNlIHR3byBudW1iZXJzLgoKLSAgIGBDb3JuZXJQZXJjYCAtIFVzZSBIYXJyaXMgQ29ybmVyIERldGVjdGlvbiBhbGdvcml0aG0gdG8gZGV0ZWN0IHRoZQogICAgY29ybmVyIGluIHRoZSBhcnR3b3Jrcy4gQ29ybmVyIGlzIHRoZSBpbnRlcnNlY3Rpb24gb2YgdHdvIGVkZ2VzLCBpdAogICAgcmVwcmVzZW50cyBhIHBvaW50IGluIHdoaWNoIHRoZSBkaXJlY3Rpb25zIG9mIHRoZXNlIHR3byBlZGdlcwogICAgY2hhbmdlLiBIZW5jZSwgdGhlIGdyYWRpZW50IG9mIHRoZSBpbWFnZSAoaW4gYm90aCBkaXJlY3Rpb25zKSBoYXZlIGEKICAgIGhpZ2ggdmFyaWF0aW9uLCB3aGljaCBjYW4gYmUgdXNlZCB0byBkZXRlY3QgaXQuIFdpdGggdGhhdCwgd2UgY2FuCiAgICBjYWxjdWxhdGUgdGhlIHJhdGlvIG9mIHBpeGVscyBhcyBjb3JuZXJzIGluIHRoZSBmdWxsIGltYWdlLgoKLSAgIGBFZGdlUGVyYCAtIFVzZSBDYW5ueSBFZGdlIERldGVjdGlvbiBhbGdvcml0aG0gdG8gZGV0ZWN0IHRoZSBlZGdlcwogICAgaW4gdGhlIGltYWdlLiBBbmQgdGhlbiBjYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugb2YgcGl4ZWxzIHJlY29nbml6ZWQKICAgIGFzIGVkZ2VzIGluIHRoZSB3aG9sZSBwaWN0dXJlLgoKLSAgIGBGYWNlQ291bnRgIC0gTnVtYmVyIG9mIGZhY2VzIGluIGFuIGFydHdvcmsncyBpbWFnZXMKCi0gICBgU29sZCBUaW1lYCAtIFdoZW4gdGhlIGF1Y3Rpb24gc2FsZXMgaGFwcGVuZWQuCgotICAgYFByaWNlYCAtIEFtb3VudCBhcnR3b3JrIHNvbGQgZm9yIGluIFVTIERvbGxhcnMgKFwkKSA8YnI+IFRoZQogICAgZmVhdHVyZXMgdGhhdCBtYXkgYmUgaGVscGZ1bCBpbiBwcm9kdWNpbmcgYSBtb2RlbCBhcmUgYG1hdGVyaWFsYCwKICAgIGBoZWlnaHRgLCBgd2lkdGhgLCBgZG9taW5hbnRDb2xvcmAsIGBicmlnaHRuZXNzYCwKICAgIGByYXRpb1VuaXF1ZUNvbG9yc2AsIGB0aHJlc2hvbGRCbGFja1BlcmNgLCBgaGlnaGJyaWdodG5lc3NQZXJjYCwKICAgIGBsb3dicmlnaHRuZXNzUGVyY2AsIGBDb3JuZXJQZXJgLCBgRWRnZVBlcmAsIGFuZCBgRmFjZUNvdW50YC4KCiMjIERhdGEgUHJlcGFyYXRpb24KCmBgYHtyIExvYWQgUGFja2FnZXMsIGVjaG89VFJVRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KG5hbmlhcikgIyBMb2FkIG5hbmFpciBmb3IgbWlzc2luZyBkYXRhIHZpc3VhbGl6YXRpb24KbGlicmFyeShPbmVSKSAKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkoZHBseXIpCmBgYAoKYGBge3IgUmVhZCBpbiBEYXRhICYgTG9vayBhdCB0aGUgU3RydWN0dXJlLCBlY2hvPVRSVUV9CiMgUmVhZCBpbiB0aGUgZGF0YSAKZGF0YSA8LSByZWFkLmRlbGltKCJ+L0Rlc2t0b3AvTWFjaGluZUxlYXJuaW5nL0ZpbmFsIFByb2plY3QvZGF0YS50eHQiKQoKIyBUYWtlIG91dCBkYXRhIHRoYXQncyBub3QgbmVlZGVkIChYLCB5ZWFyT2ZCaXJ0aCwgeWVhck9mRGVhdGgsIHNvbGRUaW1lKQphcnRfZGF0YSA8LSBkYXRhWywgYygyOjMsIDY6MTEsIDE0OjIyKV0KIyBSZW1vdmVkIGEgcm93IGR1ZSB0byBkYXRhIGJlaW5nIGluYWNjdXJhdGUuIAphcnRfZGF0YSA8LSBhcnRfZGF0YVstMzQ1NDEsIF0KCiMgTG9vayBhdCB0aGUgc3RydWN0dXJlIG9mIGFydF9kYXRhIApzdHIoYXJ0X2RhdGEpCnN1bW1hcnkoYXJ0X2RhdGEkcHJpY2UpCmBgYAoKU29tZSByb3dzIGluIG91ciBkYXRhIGFyZSBlbXB0eSBidXQgbm90IHNldCB0byBOL0EuIE5lZWQgdG8gY29udmVydAp0aG9zZSBlbXB0eSB2YWx1ZXMgdG8gTi9BCgpgYGB7cn0KYXJ0X2RhdGEgPC0gYXMuZGF0YS5mcmFtZShsYXBwbHkoYXJ0X2RhdGEsIGZ1bmN0aW9uKHgpIHsKICBpZmVsc2UoeCA9PSAiIiwgTkEsIHgpCn0pKQpgYGAKCiMjIERhdGEgVmlzdWFsaXphdGlvbgoKVmlzdWFsaXplIG91ciByZXNwb25zZSB2YXJpYWJsZSwgUHJpY2UgRHVlIHRvIHRoZSBsYXJnZSByYW5nZSBvZiB2YWx1ZXMKaW4gUHJpY2UsIHdlIGRlY2lkZWQgdG8gdGFrZSB0aGUgbmF0dXJhbCBsb2cgb2YgcHJpY2UgJGxvZyhwcmljZSArIDEpJC4KVGhpcyB3aWxsIGhlbHAgYmV0dGVyIHZpc3VhbGl6ZSBwcmljZS4KCmBgYHtyfQphcnRfZGF0YSRsb2dfcHJpY2UgPC0gbG9nKGFydF9kYXRhJHByaWNlICsgMSkKCmdfMSA8LSBnZ3Bsb3QoYXJ0X2RhdGEsIGFlcyh4ID0gbG9nX3ByaWNlKSkgKwogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKwogICB0aGVtZV9zZXQodGhlbWVfYncoYmFzZV9zaXplID0gMjIpICkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksICMgUmVtb3ZlIGdyaWQKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAjIFJlbW92ZSBncmlkCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLCAjIFJlbW92ZSBncmlkCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAjIFJlbW92ZSBncmlkIAogIGxhYnMoeCA9ICJsb2coUHJpY2UgKyAxKSIsIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBMb2cgUHJpY2UiKQpnXzEKc3VtbWFyeShhcnRfZGF0YSRwcmljZSkKYGBgCgoqKlRha2luZyB0aGUgbG9nKFByaWNlICsgMSksIHRoaXMgaGVscGVkIHJlZHVjZSB0aGUgc2tldyBvZiBQcmljZS4gTWFraW5nIGl0IGVhc2llciB0byB2aXN1YWxpemUqKiA8YnI+IDxicj4KCkxvb2sgYXQgdGhlIG1pc3NpbmcgdmFsdWVzIGluIG91ciBkYXRhLi4uCgpgYGB7cn0KIyBWaXN1YWxpemUgbWlzc2luZyBmZWF0dXJlcwpmZWF0X3ZhcnMgPC0gbmFtZXMoYXJ0X2RhdGEpW2MoNCwgNjoxNyldCnZpc19taXNzKGFydF9kYXRhWywgZmVhdF92YXJzXSkKCiMgVmlzdWFsaXplIG1pc3NpbmcgZmVhdHVyZXMgd2l0aCBvdXIgcmVzcG9uc2UgdmFyaWFibGUgcHJpY2UKdF9iaW5zIDwtIGJpbihhcnRfZGF0YSRsb2dfcHJpY2UsIG5iaW5zID0gNiwgbWV0aG9kID0gImxlbmd0aCIpICMgQmluIHJlc3BvbnNlIHZhcmlhYmxlCnBsb3RfZGF0IDwtIGNiaW5kLmRhdGEuZnJhbWUodF9iaW5zLCBhcnRfZGF0YVssIGZlYXRfdmFyc10pCmdnX21pc3NfZmN0KHggPSBwbG90X2RhdCwgZmN0ID0gdF9iaW5zKSArCiAgbGFicyh4ID0gIlByaWNlIikKYGBgCgoqKlllYXIsIHdpZHRoLCBhbmQgaGVpZ2h0IGFyZSB0aGUgb25seSBmZWF0dXJlcyB0aGF0IGhhdmUgbWlzc2luZyBkYXRhLHdpdGggWWVhciBoYXZpbmcgdGhlIG1vc3QgbWlzc2luZyB2YWx1ZXMuIFllYXIgaGF2aW5nIDMyJSBtaXNzaW5nLCBtYWtpbmcgaXQgYSBmZWF0dXJlIHdlIHdvbid0IHVzZSBmb3IgYnVpbGRpbmcgb3VyIG1vZGVsLioqIAo8YnI+IDxicj4KRmluZCB0aGUgbW9zdCBjb21tb24gbWF0ZXJpYWxzIHVzZWQgaW4gb3VyIGRhdGFmcmFtZS4KCmBgYHtyfQphcnRfZGF0YSRtYXRlcmlhbCA8LSBzdHJfcmVwbGFjZV9hbGwoYXJ0X2RhdGEkbWF0ZXJpYWwsICdfJywgJyAnKQoKIyBGaWd1cmUgb3V0IHRoZSBtb3N0IGNvbW1vbiB3b3Jkcy9waHJhc2UgdXNlZCBpbiBtYXRlcmlhbHMgY29sdW1uIHVzaW5nIHRoZSB0aWR5dmVyc2UKd29yZF9jb3VudCA8LSBhcnRfZGF0YSAlPiUKICB1bm5lc3RfdG9rZW5zKHdvcmQsIG1hdGVyaWFsKSAlPiUKICBhbnRpX2pvaW4oc3RvcF93b3JkcywgYnkgPSAid29yZCIpICU+JQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQoKIyBQbG90IHRvcCAxMCB3b3Jkcy9waHJhc2VzCndvcmRfY291bnQgJT4lCiAgc2xpY2VfbWF4KG4sIG4gPSAxMCkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcih3b3JkLCBuKSwgeSA9IG4pKSArCiAgZ2VvbV9jb2woKSArCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKHggPSAiV29yZCIsIHkgPSAiQ291bnQiLCB0aXRsZSA9ICJNb3N0IENvbW1vbiBXb3JkcyIpCmBgYAoqKk9pbCBpcyB0aGUgbW9zdCB1c2VkIG1hdGVyaWFsIGluIG91ciBkYXRhc2V0KiogPGJyPjxicj4KCmBgYHtyIFZpc3VhbGl6aW5nIE1hdGVyaWFscywgZWNobz1UUlVFfQojIFB1bGwgb3V0IG1ham9yIG1hdGVyaWFsIGNhdGVnb3JpZXMgbGlrZSBvaWwgCm9pbCA8LSByZXAoMCwgbnJvdyhhcnRfZGF0YSkpCm9pbFtncmVwKCJvaWwiLCBhcnRfZGF0YSRtYXRlcmlhbCldIDwtIDEKc3VtKG9pbFtncmVwKCJvaWwiLCBhcnRfZGF0YSRtYXRlcmlhbCldKQojIDEzNTExIEFydHdvcmtzIHVzZSBPaWwgYXMgYSBtYXRlcmlhbAojIENyZWF0ZSBGYWN0b3IgY29sdW1uIG9mIG9pbCAKYXJ0X2RhdGEkb2lsIDwtIGFzLmZhY3RvcihvaWwpCgojIFZpc3VhbGl6ZSBPaWwgYW5kIFByaWNlIApnXzIgPC0gZ2dwbG90KGFydF9kYXRhLCBhZXMoIHkgPSBsb2dfcHJpY2UsIHggPSBvaWwsIGZpbGwgPSBvaWwpKSArICMgU2V0IHggYW5kIGZpbGwgYXMgZGlzYWdub3NpcywgeSBhcyB2YWx1ZQogIGdlb21fYm94cGxvdCgpICsgIyBVc2UgYm94bG90CiAgICB0aGVtZV9idygpICsgIyBTZXQgdGhlbWUKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAjIFJlbW92ZSBncmlkCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgIyBSZW1vdmUgZ3JpZAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwgIyBSZW1vdmUgZ3JpZAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgIyBSZW1vdmUgZ3JpZCAKICBsYWJzKHggPSAiT2lsIiwgdGl0bGUgPSAiT2lsIHZzIFByaWNlIiwKICAgICAgIGZpbGwgPSAiT2lsIikgKyAjIFNldCBsYWJlbHMKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIxIiA9ICJyZWQiLCAiMCIgPSAiYmx1ZSIpLCAjIE1hbnVhbGx5IHNldCBmaWxsIHZhbHVlcwogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjEiID0gIk9pbCIsICIwIiA9ICJPdGhlciBNYXRlcmlhbCBVc2VkIikpCmdfMgoKIyBQdWxsIG91dCBhY3J5bGljIGluIG1hdGVyaWFsIGFuZCB2aXN1YWxpemUgCmFjcnlsaWMgPC0gcmVwKDAsIG5yb3coYXJ0X2RhdGEpKQphY3J5bGljW2dyZXAoImFjcnlsaWMiLCBhcnRfZGF0YSRtYXRlcmlhbCldIDwtIDEKYXJ0X2RhdGEkYWNyeWxpYyA8LSBhcy5mYWN0b3IoYWNyeWxpYykKCiMgVmlzdWFsaXplIEFjcnlsaWMgYW5kIFByaWNlIApnXzMgPC0gZ2dwbG90KGFydF9kYXRhLCBhZXMoIHkgPSBsb2dfcHJpY2UsIHggPSBhY3J5bGljLCBmaWxsID0gYWNyeWxpYykpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICAgIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgbGFicyh4ID0gIkFjcnlsaWMiLCB0aXRsZSA9ICJBY3J5bGljIHZzIFByaWNlIiwKICAgICAgIGZpbGwgPSAiQWNyeWxpYyIpICsgIyBTZXQgbGFiZWxzCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiMSIgPSAicmVkIiwgIjAiID0gImJsdWUiKSwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiMSIgPSAiQWNyeWxpYyIsICIwIiA9ICJPdGhlciBNYXRlcmlhbCBVc2VkIikpCmdfMwoKIyBXYXRlcmNvbG9yIAojIFB1bGwgb3V0IHdhdGVyY29sb3IgaW4gbWF0ZXJpYWwgYW5kIHZpc3VhbGl6ZSAKd2F0ZXJjb2xvciA8LSByZXAoMCwgbnJvdyhhcnRfZGF0YSkpCndhdGVyY29sb3JbZ3JlcCgid2F0ZXJjb2xvciIsIGFydF9kYXRhJG1hdGVyaWFsKV0gPC0gMQphcnRfZGF0YSR3YXRlcmNvbG9yIDwtIGFzLmZhY3Rvcih3YXRlcmNvbG9yKQoKIyBWaXN1YWxpemUgQWNyeWxpYyBhbmQgUHJpY2UgCmdfNCA8LSBnZ3Bsb3QoYXJ0X2RhdGEsIGFlcyggeSA9IGxvZ19wcmljZSwgeCA9IHdhdGVyY29sb3IsIGZpbGwgPSB3YXRlcmNvbG9yKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogICAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBsYWJzKHggPSAid2F0ZXJjb2xvciIsIHRpdGxlID0gIndhdGVyY29sb3IgdnMgUHJpY2UiLAogICAgICAgZmlsbCA9ICJ3YXRlcmNvbG9yIikgKyAjIFNldCBsYWJlbHMKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIxIiA9ICJyZWQiLCAiMCIgPSAiYmx1ZSIpLCAKICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIxIiA9ICJ3YXRlcmNvbG9yIiwgIjAiID0gIk90aGVyIE1hdGVyaWFsIFVzZWQiKSkKZ180CgojIFB1bGwgb3V0IFNjcmVlbnByaW50IGluIG1hdGVyaWFsIGFuZCB2aXN1YWxpemUgCnNjcmVlbnByaW50IDwtIHJlcCgwLCBucm93KGFydF9kYXRhKSkKc2NyZWVucHJpbnRbZ3JlcCgic2NyZWVucHJpbnQiLCBhcnRfZGF0YSRtYXRlcmlhbCldIDwtIDEKYXJ0X2RhdGEkc2NyZWVucHJpbnQgPC0gYXMuZmFjdG9yKHNjcmVlbnByaW50KQoKIyBWaXN1YWxpemUgU2NyZWVucHJpbnQgYW5kIFByaWNlIApnXzUgPC0gZ2dwbG90KGFydF9kYXRhLCBhZXMoIHkgPSBsb2dfcHJpY2UsIHggPSBzY3JlZW5wcmludCwgZmlsbCA9IHNjcmVlbnByaW50KSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogICAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBsYWJzKHggPSAic2NyZWVucHJpbnQiLCB0aXRsZSA9ICJzY3JlZW5wcmludCB2cyBQcmljZSIsCiAgICAgICBmaWxsID0gInNjcmVlbnByaW50IikgKyAjIFNldCBsYWJlbHMKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIxIiA9ICJyZWQiLCAiMCIgPSAiYmx1ZSIpLCAKICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIxIiA9ICJzY3JlZW5wcmludCIsICIwIiA9ICJPdGhlciBNYXRlcmlhbCBVc2VkIikpCmdfNQpgYGAKCjxicj4gPGJyPiAqKlRocm91Z2ggdGhlc2UgdmlzdWFsaXphdGlvbnMsIHdlIGNhbiBzZWUgdGhhdCB0aGUgTWF0ZXJpYWwncyB1c2VkIHRvIGNyZWF0ZSBhbiBhcnR3b3JrIGRvZXMgaGF2ZSBhbiBpbXBhY3Qgb24gb3VyIHJlc3BvbnNlIHZhcmlhYmxlIHByaWNlLioqIDxicj4gPGJyPgoKIyMjIFVuZGVyc3RhbmQgdGhlIHJlbGF0aW9uc2hpcCB3aXRoIHByaWNlIGFuZCBvdGhlciBmZWF0dXJlcyBieSB2aXN1YWxpemF0aW9uLgoKPGJyPiBCcmlnaHRuZXNzIGFuZCBMb2dfUHJpY2UKCmBgYHtyfQpnXzcgPC0gZ2dwbG90KGFydF9kYXRhLAogICAgICAgICAgICAgIGFlcyh5ID0gbG9nX3ByaWNlLCAKICAgICAgICAgICAgICAgICAgeCA9IGJyaWdodG5lc3MpKSArIAogIGdlb21fcG9pbnQoY29sb3IgPSAiYmx1ZSIsIGFscGhhID0gMC4xMCkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKSArCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHkgPSAiUHJpY2UiLCAjIFNldCBwbG90IGxhYmVscwogICAgICAgeCA9ICJCcmlnaHRuZXNzIiwKICAgICAgIHRpdGxlID0gIkJyaWdodG5lc3Mgb2YgQXJ0d29yayB2cyBMb2dfUHJpY2UiKQpnXzcKYGBgCgoqKlRoZSBCcmlnaHRuZXNzIG9mIGFuIGFydHdvcmsgc2VlbXMgdG8gaGF2ZSBhIHNvbWV3aGF0IHBvc2l0aXZlIHJlbGF0aW9uc2hpcCB3aXRoIHRoZSBsb2dfcHJpY2UqKiA8YnI+IDxicj4gRmFjZSBDb3VudCB2cyBQcmljZQoKYGBge3J9CmFydF9kYXRhJEZhY2VDb3VudCA8LSBhcy5mYWN0b3IoYXJ0X2RhdGEkRmFjZUNvdW50KQpnXzggPC0gZ2dwbG90KGFydF9kYXRhLCBhZXMoIHkgPSBsb2dfcHJpY2UsIHggPSBGYWNlQ291bnQsIGZpbGwgPSBGYWNlQ291bnQpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQpnXzgKYGBgCgoqKlRoZSBmYWNlIGNvdW50IG9mIGFuIGFydHdvcmsgc2VlbXMgdG8gaGF2ZSBhIHJlbGF0aW9uc2hpcCB3aXRoIGxvZ19wcmljZSwgYnV0IHRoZXJlIGFyZSBtYW55IG91dGxpZXJzIHdpdGggbG9nX3ByaWNlIGFuZCBGYWNlQ291bnQqKgo8YnI+IDxicj4gRG9taW5hbnQgQ29sb3IgdnMgUHJpY2UKCmBgYHtyfQphcnRfZGF0YSRkb21pbmFudENvbG9yIDwtIGFzLmZhY3RvcihhcnRfZGF0YSRkb21pbmFudENvbG9yKQpnXzkgPC0gZ2dwbG90KGFydF9kYXRhLCBhZXMoIHkgPSBsb2dfcHJpY2UsIHggPSBkb21pbmFudENvbG9yLCBmaWxsID0gZG9taW5hbnRDb2xvcikpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICAgIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCmdfOQpgYGAKKipEb21pbmFudCBDb2xvciBzZWVtcyB0byBwbGF5IGEgcm9sZSBpbiB0aGUgcHJpY2Ugb2YgYW4gYXJ0d29yaywgYnV0IHRoZXJlIGFyZSBtYW55IG91dGxpZXJzLioqClJhdGlvIG9mIFVuaXF1ZSBDb2xvcnMgdnMgUHJpY2UKCmBgYHtyfQpnXzEwIDwtIGdncGxvdChhcnRfZGF0YSwKICAgICAgICAgICAgICBhZXMoeSA9IGxvZ19wcmljZSwgCiAgICAgICAgICAgICAgICAgIHggPSByYXRpb1VuaXF1ZUNvbG9ycykpICsgCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIiwgYWxwaGEgPSAwLjEwKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScpICsKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnMoeSA9ICJQcmljZSIsICMgU2V0IHBsb3QgbGFiZWxzCiAgICAgICB4ID0gIlJhdGlvIFVuaXF1ZSBDb2xvcnMiLAogICAgICAgdGl0bGUgPSAiUmF0aW8gb2YgVW5pcXVlIENvbG9ycyB2cyBMb2dfUHJpY2UiKQpnXzEwCmBgYAoKKipUaGVyZSBzZWVtcyB0byBiZSBhIHNvbWV3aGF0IG5lZ2F0aXZlIHJlbGF0aW9uc2hpcCB3aXRoIHRoZSBSYXRpbyBvZiBVbmlxdWUgQ29sb3JzIGFuZCB0aGUgUHJpY2Ugb2YgYW4gQXJ0d29yay4gVGhlIGxlc3MgY29sb3JzIHVzZWQgaW4gYW4gYXJ0d29yayBvciB0aGUgaGlnaGVyIHJhdGlvIG9mIHVuaXF1ZSBjb2xvcnMsIHRoZSBwcmljZSBvZiBhbiBhcnR3b3JrIGRlY3JlYXNlcyoqIDxicj4gClZpc3VhbGl6ZSBIZWlnaHQvV2lkdGggd2l0aCBQcmljZQpgYGB7cn0KYXJ0X2RhdGEkd2lkdGggPC0gYXMubnVtZXJpYyhhcnRfZGF0YSR3aWR0aCkKYXJ0X2RhdGEkaGVpZ2h0IDwtIGFzLm51bWVyaWMoYXJ0X2RhdGEkaGVpZ2h0KQoKIyBEZWNpZGVkIHRvIHRha2UgdGhlIGxvZyBvZiB3aWR0aCAmIGhlaWdodCBvZiBhbiBhcnR3b3JrIHRvIHJlZHVjZSB0aGUgc2tldy4uLgpnXzEyIDwtIGdncGxvdChhcnRfZGF0YSwKICAgICAgICAgICAgICBhZXMoeSA9IGxvZ19wcmljZSwgCiAgICAgICAgICAgICAgICAgIHggPSBsb2cod2lkdGgpKSkgKyAKICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiLCBhbHBoYSA9IDAuMTApICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJykgKwogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh5ID0gIkxvZ19QcmljZSIsICMgU2V0IHBsb3QgbGFiZWxzCiAgICAgICB4ID0gImxvZyhXaWR0aCkgb2YgQXJ0d29yayIsCiAgICAgICB0aXRsZSA9ICJMb2dfV2lkdGggdnMgTG9nX1ByaWNlIikKZ18xMgpnXzEzIDwtIGdncGxvdChhcnRfZGF0YSwKICAgICAgICAgICAgICBhZXMoeSA9IGxvZ19wcmljZSwgCiAgICAgICAgICAgICAgICAgIHggPSBsb2coaGVpZ2h0KSkpICsgCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIiwgYWxwaGEgPSAwLjEwKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScpICsKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnMoeSA9ICJMb2dfUHJpY2UiLCAjIFNldCBwbG90IGxhYmVscwogICAgICAgeCA9ICJsb2coSGVpZ2h0KSBvZiBBcnR3b3JrIiwKICAgICAgIHRpdGxlID0gIkxvZ19IZWlnaHQgdnMgTG9nX1ByaWNlIikKZ18xMwpnXzE0IDwtIGdncGxvdChhcnRfZGF0YSwKICAgICAgICAgICAgICBhZXMoeSA9IGxvZ19wcmljZSwgCiAgICAgICAgICAgICAgICAgIHggPSBsb2coaGVpZ2h0KndpZHRoKSkpICsgCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIiwgYWxwaGEgPSAwLjEwKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScpICsKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnMoeSA9ICJMb2dfUHJpY2UiLCAjIFNldCBwbG90IGxhYmVscwogICAgICAgeCA9ICJMb2dfQXJlYSBvZiBBcnR3b3JrIiwKICAgICAgIHRpdGxlID0gIkxvZ19BcmVhIHZzIExvZ19QcmljZSIpCmdfMTQKYGBgCioqVGhlcmUgc2VlbXMgdG8gYmUgYSBzb21ld2hhdCBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgd2l0aCB0aGUgYXJlYSBvZiBhbiBhcnR3b3JrIGFuZCBpdHMgcHJpY2UqKiA8YnI+PGJyPgpDcmVhdGUgYSBuZXcgY29sdW1uIHRvIGlkZW50aWZ5IGEgd2VsbCBrbm93biBhcnRpc3QgdnMgbm90IHdlbGwga25vd24KYXJ0aXN0IGJhc2VkIG9uIHRoZSBhdmVyYWdlIHByaWNlIG9mIGFydHdvcmtzLiA8YnI+VGhlcmUgYXJlIDgsNjA4IHVuaXF1ZQphcnRpc3RzIGluIHRoZSBkYXRhLXNldC4KYGBge3J9Cgp1bmlxdWVfYXJ0aXN0cyA8LSB1bmlxdWUoYXJ0X2RhdGEkYXJ0aXN0KQoKIyBVc2UgZHBseXIgcGFja2FnZSB0byBncm91cCBhcnRpc3QgYW5kIHRoZWlyIGF2ZXJhZ2UgcHJpY2UgZm9yIGFydHdvcmsgc29sZCAKYXJ0aXN0X2F2ZyA8LSBhcnRfZGF0YSAlPiUgCiAgZ3JvdXBfYnkoYXJ0aXN0KSAlPiUgCiAgc3VtbWFyaXNlKGF2Z19wcmljZSA9IG1lYW4ocHJpY2UpKQoKIyBDcmVhdGUgYXZlcmFnZSBwcmljZSBvZiBhcnR3b3JrcyBpbiB0aGUgZGF0YXNldCB0byBjcmVhdGUgYSBjdXRvZmYgb2Ygd2VsbCBrbm93biBhcnRpc3QKYXJ0X2F2Z19wcmljZSA8LSBtZWFuKGFydF9kYXRhJHByaWNlKQojIE1ha2UgYmluYXJ5IHdpdGggMCAmIDEsIG1ha2UgaXQgbnVtZXJpYyAKYXJ0aXN0X2F2ZyR3ZWxsX2tub3duIDwtIGlmZWxzZShhcnRpc3RfYXZnJGF2Z19wcmljZSA+PSBhcnRfYXZnX3ByaWNlLCAxLCAwKQoKIyBNZXJnZSB0d28gZGF0YSBmcmFtZXMgdG9nZXRoZXIsIHJlbW92ZSBhcnRpc3QgYXZlcmFnZSBwcmljZQphcnRfZGF0YSA8LSBtZXJnZShhcnRfZGF0YSwgYXJ0aXN0X2F2ZywgYnkgPSAnYXJ0aXN0JykKYXJ0X2RhdGEgPC0gYXJ0X2RhdGFbLCAtMjNdCgpzdHIoYXJ0X2RhdGEkd2VsbF9rbm93bikKc3VtbWFyeShhcnRfZGF0YSR3ZWxsX2tub3duKQpgYGAKClZpc3VhbGl6ZSBBcnR3b3JrIFByaWNlIGFuZCBpZiB0aGUgQXJ0aXN0IGlzIGNvbnNpZGVyZWQgd2VsbCBrbm93biBvciBub3QuCgpgYGB7cn0KYXJ0X2RhdGEkd2VsbF9rbm93biA8LSBhcy5mYWN0b3IoYXJ0X2RhdGEkd2VsbF9rbm93bikKZ18xMSA8LSBnZ3Bsb3QoYXJ0X2RhdGEsIGFlcyggeSA9IGxvZ19wcmljZSwgeCA9IHdlbGxfa25vd24sIGZpbGwgPSB3ZWxsX2tub3duKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIAogICAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKZ18xMQpgYGAKCioqV2VsbCBrbm93biBhcnRpc3RzIGhhdmUgYSBoaWdoZXIgYXZlcmFnZSBhcnR3b3JrIHByaWNlIGNvbXBhcmVkIHRvCmxlc3NlciBrbm93biBhcnRpc3RzLioqCgpgYGB7cn0KIyBzYXZlKGFydF9kYXRhLCBhcnRpc3RfYXZnLCBwbG90X2RhdCwgd29yZF9jb3VudCwgZGF0YSwKIyAgICAgZmlsZSA9ICJmaW5hbF9wcm9qZWN0X2RhdGEuUkRhdGEiKQpgYGAKCgoKYGBge3J9CnVuaXF1ZV9hcnRpc3RzIDwtIHVuaXF1ZShhcnRfZGF0YSRhcnRpc3QpCgojIFVzZSBkcGx5ciBwYWNrYWdlIHRvIGdyb3VwIGFydGlzdCBhbmQgdGhlaXIgYXZlcmFnZSBwcmljZSBmb3IgYXJ0d29yayBzb2xkIAphcnRpc3RfYXZnIDwtIGFydF9kYXRhICU+JSAKICBncm91cF9ieShhcnRpc3QpICU+JSAKICBzdW1tYXJpc2UoYXZnX3ByaWNlID0gbWVhbihwcmljZSkpCgojIENyZWF0ZSBhdmVyYWdlIHByaWNlIG9mIGFydHdvcmtzIGluIHRoZSBkYXRhc2V0IHRvIGNyZWF0ZSBhIGN1dG9mZiBvZiB3ZWxsIGtub3duIGFydGlzdAphcnRfYXZnX3ByaWNlIDwtIHF1YW50aWxlKGFydF9kYXRhJHByaWNlLCAwLjkpCiMgTWFrZSBiaW5hcnkgd2l0aCAwICYgMSwgbWFrZSBpdCBudW1lcmljIAphcnRpc3RfYXZnJHdlbGxfa25vd24gPC0gaWZlbHNlKGFydGlzdF9hdmckYXZnX3ByaWNlID49IGFydF9hdmdfcHJpY2UsIDEsIDApCmFydGlzdF9hdmckd2VsbF9rbm93biA8LSBhcy5mYWN0b3IoYXJ0aXN0X2F2ZyR3ZWxsX2tub3duKQpzdW1tYXJ5KGFydGlzdF9hdmckd2VsbF9rbm93bikKCiMgTWVyZ2UgdHdvIGRhdGEgZnJhbWVzIHRvZ2V0aGVyLCByZW1vdmUgYXJ0aXN0IGF2ZXJhZ2UgcHJpY2UKYXJ0X2RhdGEyIDwtIG1lcmdlKGFydF9kYXRhLCBhcnRpc3RfYXZnLCBieSA9ICdhcnRpc3QnKQpzdW1tYXJ5KGFydF9kYXRhMiR3ZWxsX2tub3duLnkpCnNhdmUoYXJ0X2RhdGEyLCBmaWxlID0gImFydF9kYXRhMi5SRGF0YSIpCmBgYAoK